#include <audio/MIDI/MIDIOutDeviceMacOSX.h>
#include <system/Log.h>

#if NE_PLATFORM == NE_PLATFORM_MACOSX

namespace ne
{
    MIDIOutDeviceMacOSX::MIDIOutDeviceMacOSX(const uint32_t id, const std::string &name)
        : MIDIOutDevice(id, name),
          mClient(0),
          mDestination(0),
          mOutPort(0)
    {
        _create();
    }

    bool MIDIOutDeviceMacOSX::open()
    {
        if (isOpen()) return true;

        Log::Out("MIDIGetNumberOfDevices[%d]", MIDIGetNumberOfDevices());
        for (uint32_t d=0; d<MIDIGetNumberOfDevices(); ++d)
        {
            Log::Out("Device[%d]", d);
            MIDIDeviceRef device = MIDIGetDevice(d);
            for (uint32_t e=0; e<MIDIDeviceGetNumberOfEntities(device); ++e)
            {
                Log::Out("    Entity[%d]", e);
                MIDIEntityRef entity = MIDIDeviceGetEntity(device, e);
                for (uint32_t i=0; i<MIDIEntityGetNumberOfDestinations(entity); ++i)
                {
                    Log::Out("        Destination[%d]", i);
                    mDestination = MIDIEntityGetDestination(entity, i);
                }
            }
        }

        CFStringRef deviceName = CFStringCreateWithCString(0, "MIDI Out Device Client", kCFStringEncodingASCII);
        OSStatus status = MIDIClientCreate(deviceName, NotifyCallback, this, &mClient);
        CFRelease(deviceName);

        CFStringRef outName = CFStringCreateWithCString(0, "MIDI Out Device OutPort", kCFStringEncodingASCII);
        status = MIDIOutputPortCreate(mClient, outName, &mOutPort);
        CFRelease(outName);

        mIsOpen = true;

        return true;
    }

    bool MIDIOutDeviceMacOSX::reset()
    {
        OSStatus status = MIDIRestart();
        if (status)
        {
            Log::Out(LL_ERROR, "MIDIOutDeviceMacOSX::reset");
            return false;
        }
        return true;
    }

    bool MIDIOutDeviceMacOSX::close()
    {
        if (!isOpen()) return true;

        MIDIPortDispose(mOutPort);
        MIDIClientDispose(mClient);

        mIsOpen = false;
        return true;
    }

    bool MIDIOutDeviceMacOSX::sendEvent(const byte event, const byte parameter1, const byte parameter2)
    {
        MIDIPacket packet;
        packet.timeStamp = 0;
        packet.length = 3;
        packet.data[0] = event;
        packet.data[1] = parameter1;
        packet.data[2] = parameter2;

        MIDIPacketList packetList;
        packetList.numPackets = 1;
        packetList.packet[0] = packet;

        OSStatus status = MIDISend(mOutPort, mDestination, &packetList);
        status = MIDIFlushOutput(mDestination);
        if (status)
        {
            Log::Out(LL_ERROR, "MIDIOutDeviceMacOSX::sendEvent");
            return false;
        }
        return true;
    }

    bool MIDIOutDeviceMacOSX::sendSysExEvent(const byte *buffer, const uint32_t length)
    {
        MIDISysexSendRequest request;
        request.destination = mDestination;
        request.data = buffer;
        request.bytesToSend = length;
        request.complete = false;
        request.completionProc = CompletionCallback;
        request.completionRefCon = this;

        OSStatus status = MIDISendSysex(&request);
        if (status)
        {
            Log::Out(LL_ERROR, "MIDIOutDeviceMacOSX::sendSysExEvent");
            return false;
        }
        return true;
    }

    MIDIOutDeviceMacOSX::~MIDIOutDeviceMacOSX()
    {
        _destroy();
    }

    
    bool MIDIOutDeviceMacOSX::_create()
    {
        return true;
    }

    bool MIDIOutDeviceMacOSX::_destroy()
    {
        return close();
    }

    void MIDIOutDeviceMacOSX::NotifyCallback(const MIDINotification *message, void *refCon)
    {
        Log::Out("MIDIOutDeviceMacOSX::NotifyCallback");
    }

    void MIDIOutDeviceMacOSX::ReadCallback(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
    {
        Log::Out("MIDIOutDeviceMacOSX::ReadCallback");
    }

    void MIDIOutDeviceMacOSX::CompletionCallback(MIDISysexSendRequest *request)
    {
        Log::Out("MIDIOutDeviceMacOSX::CompletionCallback");
    }
}

#endif
